package com.yami.shop.mystery.common.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yami.shop.bean.model.Product;
import com.yami.shop.common.exception.YamiShopBindException;
import com.yami.shop.common.util.RedisUtil;
import com.yami.shop.mystery.common.dao.ActivityProdMapper;
import com.yami.shop.mystery.common.dto.ActivityProdDto;
import com.yami.shop.mystery.common.model.ActivityProd;
import com.yami.shop.mystery.common.model.MysteryBox;
import com.yami.shop.mystery.common.service.ActivityProdService;
import com.yami.shop.mystery.common.service.MysteryBoxService;
import com.yami.shop.service.ProductService;
import lombok.AllArgsConstructor;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 活动扩展业务实现类
 *
 * @author wxf
 * @date 2023/4/12 10:59
 */
@Service
@AllArgsConstructor
public class ActivityProdServiceImpl extends ServiceImpl<ActivityProdMapper, ActivityProd> implements ActivityProdService {

    private final ActivityProdMapper prodMapper;

    private final MysteryBoxService mysteryBoxService;

    private final ProductService productService;

    private final RedissonClient redissonClient;

    private final static String ACTIVITY_SKU_STOCKS_PREFIX = "ACTIVITY_SKU_STOCKS_";

    private static final String REDISSON_LOCK_PREFIX = "redisson_lock:";

    /**
     * 修改报名注水量
     *
     * @param activityProd
     */
    @Override
    public void updateWaterSoldNum(ActivityProd activityProd) {
        prodMapper.updateWaterSoldNum(activityProd);
    }

    /**
     * 通过活动id查询商品列表
     *
     * @param activityId
     * @return
     */
    @Override
    public List<ActivityProd> listByActivityId(Long activityId) {
        return prodMapper.listByActivityId(activityId);
    }

    @Override
    public List<ActivityProd> listByActivityId(Long activityId, Long blindType) {
        List<ActivityProd> activityProds = prodMapper.listByActivityId(activityId);
        // 回显商品信息
        for(ActivityProd activityProd:activityProds){
            // 盲盒信息
            if(blindType == 1){
                MysteryBox detail = mysteryBoxService.detail(activityProd.getProdId());
                activityProd.setProdName(detail.getBoxName());
                activityProd.setPic(detail.getPic());
                activityProd.setProdId(detail.getId());
            }else if(blindType == 0){
                Product product = productService.getProductById(activityProd.getProdId());
                activityProd.setProdName(product.getProdName());
                activityProd.setPic(product.getPic());
                activityProd.setProdId(product.getProdId());
            }
        }
        return activityProds;
    }

    @Override
    public void decrActivitySkuStocks(Long activityId, Integer prodCount, Long prodId) {
        String key = ACTIVITY_SKU_STOCKS_PREFIX + activityId+"_"+prodId;

        Long cacheStocks = RedisUtil.getLongValue(key);

        if (cacheStocks != null && cacheStocks <= 0) {
            // 库存不足
            throw new YamiShopBindException("yami.insufficient.inventory");
        }

        // 如果没有库存就缓存一个库存
        if (cacheStocks == null || RedisUtil.getExpire(key) < 1) {

            // 加锁，防止缓存击穿
            RLock rLock = redissonClient.getLock(REDISSON_LOCK_PREFIX + ":getActivitySkuStocks");
            try {
                int lockWait = 10;
                if (rLock.tryLock(lockWait, lockWait, TimeUnit.SECONDS)) {
                    // 再获取一遍缓存
                    cacheStocks = RedisUtil.getLongValue(key);
                    if (cacheStocks == null) {
                        this.removeActivitySkuCache(activityId, prodId);
                        Integer prodTotalStocks = this.getActivitySkuByActivityIdAndProdId(activityId,prodId).getProdTotalStocks();
                        if (prodTotalStocks > 0) {
                            RedisUtil.setLongValue(key, Long.valueOf(prodTotalStocks), -1);
                        } else {
                            RedisUtil.setLongValue(key, 0L, 30);
                        }
                        cacheStocks = Long.valueOf(prodTotalStocks);
                    }
                } else {
                    // 网络异常
                    throw new YamiShopBindException("yami.network.exception");
                }
            } catch (InterruptedException e) {
                log.error("InterruptedException:", e);
            } finally {
                try {
                    if (rLock.isLocked()) {
                        rLock.unlock();
                    }
                } catch (Exception e) {
                    log.error("Exception:", e);
                }
            }
        }

        if (cacheStocks == null || cacheStocks < prodCount || RedisUtil.decr(key, prodCount) < 0) {
            RedisUtil.expire(key, 30);
            // 本轮商品已被秒杀完毕，还有用户未支付，还有机会哟
            throw new YamiShopBindException("yami.seckill.complete");
        }
    }


    @Override
    @Cacheable(cacheNames = "ActivitySkuByActivityIdAndProdId", key = "#activityId + ':' + #prodId")
    public ActivityProd getActivitySkuByActivityIdAndProdId(Long activityId, Long prodId) {
        return prodMapper.getActivitySkuByActivityIdAndProdId(activityId,prodId);
    }

    @Override
    @Caching(evict = {
            @CacheEvict(cacheNames = "ActivitySkuByActivityIdAndProdId", key = "#activityId + ':' + #prodId"),
    })
    public void removeActivitySkuCache(Long activityId, Long prodId) {
    }

    @Override
    public int updateStocks(Long activityId, Long prodId, Integer prodCount) {
        return prodMapper.updateStocks(activityId,prodId,prodCount);
    }

    @Override
    public int returnStocks(Long activityId, Long prodId, Integer prodCount) {
        return prodMapper.returnStocks(activityId, prodId, prodCount);
    }
}
